﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using DistributedSystems.Object;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace DistributedSystems
{
    public partial class WebForm1 : System.Web.UI.Page
    {
        DateTime startDay;
        DateTime finishDay;
        int postCodeOrigin;
        int postCodeDestination;
        float totalFee = 0;
        int location;
        int numTrunk = 0;
        DistributedSystemsDataContext db = new DistributedSystemsDataContext();

        protected void Page_Load(object sender, EventArgs e)
        {
            if (IsPostBack == false)
            {
                if (this.PreviousPage.IsCrossPagePostBack == true)
                {
                    txtUserNameInput.Text = ((TextBox)this.PreviousPage.FindControl("txtUserNameInput")).Text;
                    txtPostCodeOrigin.Text = ((TextBox)this.PreviousPage.FindControl("txtPostCodeOrigin")).Text;
                    txtPostCodeInput.Text = ((TextBox)this.PreviousPage.FindControl("txtPostCodeInput")).Text;
                    txtPhoneNumberInput.Text = ((TextBox)this.PreviousPage.FindControl("txtPhoneNumberInput")).Text;
                    txtOriginAddressInput.Text = ((TextBox)this.PreviousPage.FindControl("txtOriginAddressInput")).Text;
                    txtNumTrunksInput.Text = ((TextBox)this.PreviousPage.FindControl("txtNumTrunksInput")).Text;
                    txtDestinationAddressInput.Text = ((TextBox)this.PreviousPage.FindControl("txtDestinationAddressInput")).Text;
                    tbStartDay.Text = ((TextBox)this.PreviousPage.FindControl("tbNgayDi")).Text;
                    tsStartDilivery.Date = ((MKB.TimePicker.TimeSelector)this.PreviousPage.FindControl("tsStartDilivery")).Date; 
  
                   
                }                
            }
            //////////////////
            DateTime temp = DateTime.Parse(tbStartDay.Text);
            startDay = new DateTime(temp.Year, temp.Month, temp.Day, tsStartDilivery.Hour, tsStartDilivery.Minute, tsStartDilivery.Second);
            postCodeOrigin = int.Parse(txtPostCodeOrigin.Text);
            postCodeDestination = int.Parse(txtPostCodeInput.Text);

            int deliveryTime = HtmlUtils.CalculatorTime(postCodeOrigin, postCodeDestination);
            temp = startDay;
            finishDay = temp.AddMinutes(deliveryTime);
            tbFinishDay.Text = finishDay.ToShortDateString() + " " + finishDay.ToShortTimeString();
            location = db.Locations.SingleOrDefault(p => p.State == PostcodeUtils.GetState(postCodeOrigin)).ID;
            numTrunk = int.Parse(txtNumTrunksInput.Text);

            totalFee = calculateTotalFee(location, numTrunk, startDay, finishDay, deliveryTime);
            txtFee.Text = totalFee.ToString() + " $";
            txtGST.Text = (totalFee * 0.1).ToString() + " $";
            txtTotalFee.Text = (totalFee * 1.1).ToString() + " $";
           
        }

        protected void btnSubmit_Click(object sender, EventArgs e)
        {
            Invoice invoice = CreateObject();
            MakePDF(invoice);
        }

        #region help
        protected Invoice CreateObject()
        {
            Invoice result = new Invoice();
            int invCode = 1;
            try
            {
                invCode = db.Invoices.Max(p => p.ID) + 1;
            }
            catch
            {
            }            
            result.AddressDes = txtDestinationAddressInput.Text;
            result.AddressOri = txtOriginAddressInput.Text;
            result.Code = String.Format("{0:000000}", invCode);
            result.CurrentTime = DateTime.Now;
            result.DeliveryDateDes = finishDay;
            result.DeliveryDateOri = startDay;
            result.GST = totalFee * 0.1;
            result.NumTrunk = numTrunk;
            
            result.PCodeDesID = db.PostCodes.Where(p => p.PCode == postCodeDestination).FirstOrDefault().ID;
            result.PCodeOriID = db.PostCodes.Where(p => p.PCode == postCodeOrigin).FirstOrDefault().ID;
            result.PhoneNumber = txtPhoneNumberInput.Text;
            result.TotalCost = totalFee;
            result.UserName = txtUserNameInput.Text;
            db.Invoices.InsertOnSubmit(result);
            db.SubmitChanges();
            return result;
        }
        protected void MakePDF(Invoice inv)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                Document document = new Document(PageSize.A4, 25, 25, 30, 30);
                PdfWriter writer = PdfWriter.GetInstance(document, ms);
                document.Open();
                document.Add(new Paragraph("Distributed Systems"));
                RomanList romanlist = new RomanList(true, 14);
                romanlist.IndentationLeft = 30f;

                romanlist.Add(string.Format("Invoice Number : {0}", inv.Code));
                romanlist.Add(string.Format("Business address :{0}", inv.AddressOri));
                romanlist.Add(string.Format("Customer's billing address :{0}", inv.AddressDes));
                romanlist.Add(string.Format("Current system time : {0}",inv.CurrentTime.ToString()));
                romanlist.Add(string.Format("Truncks : {0}",inv.NumTrunk.ToString()));
                romanlist.Add(string.Format("Begin delivery date and time : {0}",inv.DeliveryDateOri.ToString()));
                romanlist.Add(string.Format("Finish delivery date and time : {0}", inv.DeliveryDateDes.ToString()));
                romanlist.Add(string.Format("Total Cost :{0} $",inv.TotalCost.ToString()));
                romanlist.Add(string.Format("GST : {0} $",inv.GST));

                document.Add(romanlist);
                document.Close();
                writer.Close();
                Response.ContentType = "pdf/application";
                Response.AddHeader("content-disposition",
                "attachment;filename=Report.pdf");
                Response.OutputStream.Write(ms.GetBuffer(), 0, ms.GetBuffer().Length);
            }
        }
        #endregion

        #region Check input

        protected bool CheckPostcodeAndAdress(string address, int postcode)
        {
            PostCode pcResult = db.PostCodes.Where(p => p.PCode == postcode
                                                    && p.Locality.Contains(address)).SingleOrDefault();
            return pcResult == null ? false : true;
        }
        #endregion

        #region Calculate fee
        // Check date is weekend or weekday
        private int checkDayType(DateTime day, List<AppendixB> lstHoliday)
        {
            int dayType = 2;
            foreach (AppendixB holiday in lstHoliday)
            {
                if (isHoliday(holiday.HolidayID.Value, startDay))
                {
                    dayType = 0;
                    break;
                }
            }

            if (dayType != 0)
                if (day.DayOfWeek == DayOfWeek.Saturday || day.DayOfWeek == DayOfWeek.Sunday)
                    dayType = 1;

            return dayType;
        }

        private bool isHoliday(int holidayID, DateTime day)
        {
            Holiday holiday = db.Holidays.SingleOrDefault(p => p.ID == holidayID);
            if (holiday.Value != null)
            {
                if (day.Day == holiday.Value.Value.Day && day.Month == holiday.Value.Value.Month)
                    return true;

                return false;
            }
            else
            {
                switch (holidayID)
                {
                    case 3:
                        return isRoyalHobartRegatta(day);
                    case 4:
                        return isLabourDayMarch(day);
                    case 5:
                        return isAdelaideCup(day);
                    case 6:
                        return isCanberraDay(day);
                    case 7:
                        return isEightHoursDay(day);
                    case 8:
                        return isLabourDay2ndMondayMarch(day);
                    case 9:
                        return isGoodFriday(day);
                    case 10:
                        return isEasterSaturday(day);
                    case 11:
                        return isEasterSunday(day);
                    case 12:
                        return isEasterMonday(day);
                    case 13:
                        return isEasterTuesday(day);
                    case 15:
                        return isLabourDayMay(day);
                    case 16:
                        return isMayDay(day);
                    case 17:
                        return isWesternAustraliaDay(day);
                    case 18:
                        return isQueenBirthday(day);
                    case 19:
                        return isPicnicDay(day);
                    case 20:
                        return isLabourDayOctober(day);
                    case 21:
                        return isRecreationDay(day);
                    case 22:
                        return isMelbourneCup(day);
                    default:
                        return false;
                }
            }
        }

        private float weekendFee(int location)
        {
            float fee = 0;
            return fee;
        }

        private float weekdayFee(int location)
        {
            float fee = 0;
            return fee;
        }

        private float calculateTotalFee(int location, int numTrunk, DateTime startDay, DateTime finishDay, int deliveryTime)
        {
            float total = numTrunk * 1000;
            AppendixA morning = db.AppendixAs.SingleOrDefault(p => p.Type == true && p.LocationID == location);
            AppendixA evening = db.AppendixAs.SingleOrDefault(p => p.Type == false && p.LocationID == location);

            List<AppendixB> lstHoliday = db.AppendixBs.Where(p => p.LocationID == location).ToList();
            List<TimeHell> lstTimeHell = DateTimeUtils.ConvertHourToListDay(startDay, deliveryTime);

            for (int i = 0; i < lstTimeHell.Count; i++)
            {
                int timeType = DateTimeUtils.CheckStateMoney(lstTimeHell[i], morning.Hour.Value); //Customize here   
                int dayType = checkDayType(lstTimeHell[i].CurentDay, lstHoliday);

                switch (timeType)
                {
                    case 1: // Morning
                        switch (dayType)
                        {
                            case 0:
                                total += morning.Holidays.Value;
                                break;
                            case 1:
                                total += morning.Weekends.Value;
                                break;
                            default:
                                total += morning.Weekdays.Value;
                                break;
                        }
                        break;
                    case 2: // Evening
                        switch (dayType)
                        {
                            case 0:
                                total += evening.Holidays.Value;
                                break;
                            case 1:
                                total += evening.Weekends.Value;
                                break;
                            default:
                                total += evening.Weekdays.Value;
                                break;
                        }
                        break;
                    case 3: // All day
                        switch (dayType)
                        {
                            case 0:
                                total += morning.Holidays.Value;
                                total += evening.Holidays.Value;
                                break;
                            case 1:
                                total += morning.Weekends.Value;
                                total += evening.Weekends.Value;
                                break;
                            default:
                                total += morning.Weekdays.Value;
                                total += evening.Weekdays.Value;
                                break;
                        }
                        break;
                }                
            }

            return total;
        }
        #endregion

        #region Calculate DateTime
        // 2nd Monday in February
        private bool isRoyalHobartRegatta(DateTime day)
        {
            if (day.Month == 2 && day.DayOfWeek == DayOfWeek.Monday && day.Day >= 8 && day.Day <= 14)
                return true;

            return false;
        }

        // 1st Monday in March
        private bool isLabourDayMarch(DateTime day)
        {
            if (day.Month == 3 && day.DayOfWeek == DayOfWeek.Monday && day.Day <= 7)
                return true;

            return false;
        }

        // 2nd Monday in March
        private bool isAdelaideCup(DateTime day)
        {
            if (day.Month == 3 && day.DayOfWeek == DayOfWeek.Monday && day.Day >= 8 && day.Day <= 14)
                return true;

            return false;
        }

        // 2nd Monday in March
        private bool isCanberraDay(DateTime day)
        {
            if (day.Month == 3 && day.DayOfWeek == DayOfWeek.Monday && day.Day >= 8 && day.Day <= 14)
                return true;

            return false;
        }

        // 2nd Monday in March
        private bool isEightHoursDay(DateTime day)
        {
            if (day.Month == 3 && day.DayOfWeek == DayOfWeek.Monday && day.Day >= 8 && day.Day <= 14)
                return true;

            return false;
        }

        // 2nd Monday in March
        private bool isLabourDay2ndMondayMarch(DateTime day)
        {
            if (day.Month == 3 && day.DayOfWeek == DayOfWeek.Monday && day.Day >= 8 && day.Day <= 14)
                return true;

            return false;
        }

        // 1st Monday in May
        private bool isLabourDayMay(DateTime day)
        {
            if (day.Month == 5 && day.DayOfWeek == DayOfWeek.Monday && day.Day <= 7)
                return true;

            return false;
        }

        // 1st Monday in May
        private bool isMayDay(DateTime day)
        {
            if (day.Month == 5 && day.DayOfWeek == DayOfWeek.Monday && day.Day <= 7)
                return true;

            return false;
        }

        // 1st Monday in June
        private bool isWesternAustraliaDay(DateTime day)
        {
            if (day.Month == 6 && day.DayOfWeek == DayOfWeek.Monday && day.Day <= 7)
                return true;

            return false;
        }

        // 2nd Monday in June
        private bool isQueenBirthday(DateTime day)
        {
            if (day.Month == 6 && day.DayOfWeek == DayOfWeek.Monday && day.Day >= 8 && day.Day <= 14)
                return true;

            return false;
        }

        // 1st Monday in August
        private bool isPicnicDay(DateTime day)
        {
            if (day.Month == 8 && day.DayOfWeek == DayOfWeek.Monday && day.Day <= 7)
                return true;

            return false;
        }

        // 1st Monday in October
        private bool isLabourDayOctober(DateTime day)
        {
            if (day.Month == 10 && day.DayOfWeek == DayOfWeek.Monday && day.Day <= 7)
                return true;

            return false;
        }

        // 1st Monday in November
        private bool isRecreationDay(DateTime day)
        {
            if (day.Month == 11 && day.DayOfWeek == DayOfWeek.Monday && day.Day <= 7)
                return true;

            return false;
        }

        // 1st Tuesday in November
        private bool isMelbourneCup(DateTime day)
        {
            if (day.Month == 11 && day.DayOfWeek == DayOfWeek.Tuesday && day.Day <= 7)
                return true;

            return false;
        }

        // Date of Easter Sunday
        private DateTime EasterSunday(int year)
        {
            int day = 0;
            int month = 0;

            int g = year % 19;
            int c = year / 100;
            int h = (c - (int)(c / 4) - (int)((8 * c + 13) / 25) + 19 * g + 15) % 30;
            int i = h - (int)(h / 28) * (1 - (int)(h / 28) * (int)(29 / (h + 1)) * (int)((21 - g) / 11));

            day = i - ((year + (int)(year / 4) + i + 2 - c + (int)(c / 4)) % 7) + 28;
            month = 3;

            if (day > 31)
            {
                month++;
                day -= 31;
            }

            return new DateTime(year, month, day);
        }

        // Good Friday
        private bool isGoodFriday(DateTime day)
        {
            DateTime ESDay = EasterSunday(day.Year);
            if (day == ESDay.AddDays(-2))
                return true;

            return false;
        }

        // Easter Saturday
        private bool isEasterSaturday(DateTime day)
        {
            DateTime ESDay = EasterSunday(day.Year);
            if (day == ESDay.AddDays(-1))
                return true;

            return false;
        }

        // Easter Sunday
        private bool isEasterSunday(DateTime day)
        {
            DateTime ESDay = EasterSunday(day.Year);
            if (day == ESDay)
                return true;

            return false;
        }

        // Easter Monday
        private bool isEasterMonday(DateTime day)
        {
            DateTime ESDay = EasterSunday(day.Year);
            if (day == ESDay.AddDays(1))
                return true;

            return false;
        }

        // Easter Tuesday
        private bool isEasterTuesday(DateTime day)
        {
            DateTime ESDay = EasterSunday(day.Year);
            if (day == ESDay.AddDays(2))
                return true;

            return false;
        }
        #endregion
    }
}